﻿using SuperFrame.Charts.Axis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;

namespace SuperFrame.Charts
{
    public class XAxis : AxisBase
    {
        public XAxis()
        {
        }

        protected override bool CheckType()
        {
            if (this.Type == XType.String)
                return false;
            return true;
        }

        internal void Update()
        {
            double desiredHeight = 0;
            double labelSize = 0;
            CalculateAutoInterval();
            GenerateLabels();
            if (this.ActualHeight > 0 && this.ActualWidth > 0)
            {
                double xAxisWidthStep = this.ActualWidth / ((IntervalCount > 0) ? IntervalCount : 1);
                if (this.Type == XType.String)
                    xAxisWidthStep = xAxisWidthStep + 2;
                double xAxisWidthPosition = 0;
                double minorstep = 0;
                axisLine.X2 = this.ActualWidth;
                // Rect oldRect = new Rect(0, 0, 0, 0);
                if (this.m_Labels.Count != labels.Count)
                {
                    #region 添加
                    if (this.m_Labels.Count > labels.Count)
                    {
                        int offset = this.m_Labels.Count - labels.Count;
                        for (int j = 0; j < offset; j++)
                        {
                            ContentControl label = new ContentControl();
                            label.Content = m_Labels[this.m_Labels.Count - offset - 1];
                            label.SetResourceReference(ContentControl.ContentTemplateProperty, "LabelTemplate");
                            label.Measure(new Size(this.ActualHeight, this.ActualWidth));
                            labels.Add(label);
                            Line tickLine = new Line();
                            tickLine.SetResourceReference(Line.StyleProperty, "MajorLineStyle");
                            tickLine.Measure(new Size(this.ActualHeight, this.ActualWidth));
                            tickLine.X1 = xAxisWidthPosition - (tickLine.DesiredSize.Width / 2);
                            tickLine.X2 = xAxisWidthPosition - (tickLine.DesiredSize.Width / 2);
                            tickLine.Y1 = 0;
                            tickLine.SetBinding(Line.Y2Property, new Binding("MajorLineSize") { Source = this });
                            tickLine.SetBinding(Line.VisibilityProperty, new Binding("ShowMajorTicks") { Source = this, Converter = new BooleanToVisibilityConverter() });
                            double minorWidth = xAxisWidthStep;
                            minorstep = minorWidth / (MinorTicksCount + 1);
                            for (int uk = 0; uk < this.MinorTicksCount; uk++)
                            {
                                Line minorLine = new Line();
                                minorLine.SetResourceReference(Line.StyleProperty, "MinorLineStyle");
                                minorLine.Measure(new Size(this.ActualHeight, this.ActualWidth));
                                minorLine.X1 = (xAxisWidthPosition + minorstep);
                                minorLine.X2 = (xAxisWidthPosition + minorstep);
                                minorLine.Y1 = 0;
                                minorLine.SetBinding(Line.Y2Property, new Binding("MinorLineSize") { Source = this });

                                minorTickLines.Add(minorLine);
                                this.Children.Add(minorLine);
                                minorstep += minorstep;
                            }
                            Panel.SetZIndex(tickLine, 1);
                            majorTickLines.Add(tickLine);
                            this.Children.Add(label);
                            this.Children.Add(tickLine);

                        }
                    }
                    else
                    {
                        int offset = labels.Count - this.m_Labels.Count;
                        for (int j = 0; j < offset; j++)
                        {
                            this.Children.Remove(labels[labels.Count - 1]);
                            labels.RemoveAt(labels.Count - 1);
                            for (int yk = 0; yk < this.MinorTicksCount; yk++)
                            {
                                this.Children.Remove(minorTickLines[minorTickLines.Count - 1]);
                                minorTickLines.RemoveAt(minorTickLines.Count - 1);
                            }
                            this.Children.Remove(majorTickLines[majorTickLines.Count - 1]);
                            majorTickLines.RemoveAt(majorTickLines.Count - 1);
                        }
                    }
                    for (int i = 0; i < this.m_Labels.Count; i++)
                    {
                        ContentControl label = labels[i];
                        label.Content = m_Labels[i];
                        label.Measure(new Size(this.ActualHeight, this.ActualWidth));
                        Line tickLine = majorTickLines[i];
                        double labelPadding = 0;
                        int minorCount1 = 0;
                        tickLine.X1 = xAxisWidthPosition;
                        tickLine.X2 = xAxisWidthPosition;
                        tickLine.Y1 = 0;
                        tickLine.Measure(new Size(this.ActualHeight, this.ActualWidth));
                        if (!(i == this.m_Labels.Count - 1))
                        {
                            double minorWidth = xAxisWidthStep;
                            minorstep = minorWidth / (MinorTicksCount + 1);
                            for (int j = 0; j < this.MinorTicksCount; j++)
                            {
                                Line minorLine = minorTickLines[minorCount1];
                                minorLine.X1 = (xAxisWidthPosition + minorstep * (j + 1));
                                minorLine.X2 = (xAxisWidthPosition + minorstep * (j + 1));
                                minorLine.Y1 = 0;
                                minorCount1++;
                            }
                        }
                        if (this.ShowMajorTicks)
                        {
                            labelPadding = tickLine.Y2;
                            desiredHeight = tickLine.Y2;
                        }
                        Canvas.SetLeft(label, xAxisWidthPosition - (label.DesiredSize.Width / 2));
                        Canvas.SetTop(label, desiredHeight);
                        xAxisWidthPosition += xAxisWidthStep;
                        labelSize = Math.Max(labelSize, label.DesiredSize.Height);
                    }
                    #endregion
                }


                #region 刷新
                int k = 0;
                int minorCount = 0;
                for (int i = 0; i < this.m_Labels.Count; i++)
                {
                    xAxisWidthPosition = this.DataToPoint(m_startValue + (m_Interval * k));
                    ContentControl label = labels[k];
                    label.Content = m_Labels[k];
                    label.Measure(new Size(this.ActualHeight, this.ActualWidth));
                    Line tickLine = majorTickLines[k];
                    double labelPadding = 0;
                    tickLine.Measure(new Size(this.ActualHeight, this.ActualWidth));
                    tickLine.X1 = xAxisWidthPosition;
                    tickLine.X2 = xAxisWidthPosition;
                    if (!(i == this.m_Labels.Count - 1))
                    {
                        double minorWidth = xAxisWidthStep;
                        minorstep = minorWidth / (MinorTicksCount + 1);
                        for (int j = 0; j < this.MinorTicksCount; j++)
                        {
                            Line minorLine = minorTickLines[minorCount];
                            minorLine.X1 = (xAxisWidthPosition + minorstep * (j + 1));
                            minorLine.X2 = (xAxisWidthPosition + minorstep * (j + 1));
                            minorLine.Y1 = 0;
                            minorCount++;
                        }
                    }
                    if (this.ShowMajorTicks)
                    {
                        labelPadding = tickLine.Y2;
                        desiredHeight = tickLine.Y2;
                    }
                    Canvas.SetLeft(label, xAxisWidthPosition - (label.DesiredSize.Width));
                    Canvas.SetTop(label, desiredHeight);
                    k++;
                    labelSize = Math.Max(labelSize, label.DesiredSize.Height);
                }
                #endregion
                if (this.header != null)
                {
                    header.Measure(new Size(this.ActualHeight, this.ActualWidth));
                    Canvas.SetLeft(header, (this.ActualWidth / 2) - (header.DesiredSize.Width / 2));
                    Canvas.SetTop(header, (this.ActualHeight / 2) - (header.DesiredSize.Height / 2) + desiredHeight);
                    desiredHeight += header.DesiredSize.Height + labelSize;
                }
            }
            if (this.Chart.AxisHeight < desiredHeight)
                this.Chart.AxisHeight = desiredHeight + 1;
        }

        internal void Initialize()
        {
            double desiredHeight = 0;
            double labelSize = 0;
            //if (m_MinValue == m_startValue + m_Interval)
            CalculateAutoInterval();
            GenerateLabels();
            if (this.ActualHeight > 0 && this.ActualWidth > 0)
            {
                this.Children.Clear();
                double xAxisWidthStep = this.ActualWidth / ((IntervalCount > 0) ? IntervalCount : 1);
                double xAxisWidthPosition = this.DataToPoint(m_startValue);
                double minorstep = 0;
                //m_offset = this.DataToPoint(m_MinValue + m_Interval);
                //Rect oldRect = new Rect(0, 0, 0, 0);
                axisLine = new Line();
                axisLine.X1 = 0;
                axisLine.X2 = this.ActualWidth;
                axisLine.Y1 = 0;
                axisLine.Y2 = 0;
                axisLine.SetResourceReference(Line.StyleProperty, "AxisLineStyle");
                labels = new List<ContentControl>();
                majorTickLines = new List<Line>();
                minorTickLines = new List<Line>();
                for (int i = 0; i < this.m_Labels.Count; i++)
                {
                    ContentControl label = new ContentControl();
                    label.Content = m_Labels[i];
                    label.SetResourceReference(ContentControl.ContentTemplateProperty, "LabelTemplate");
                    label.Measure(new Size(this.ActualHeight, this.ActualWidth));
                    labels.Add(label);
                    Line tickLine = new Line();
                    double labelPadding = 0;
                    tickLine.Measure(new Size(this.ActualHeight, this.ActualWidth));
                    tickLine.X1 = xAxisWidthPosition - (tickLine.DesiredSize.Width / 2);
                    tickLine.X2 = xAxisWidthPosition - (tickLine.DesiredSize.Width / 2);
                    tickLine.Y1 = 0;
                    tickLine.SetResourceReference(Line.StyleProperty, "MajorLineStyle");
                    tickLine.SetBinding(Line.Y2Property, new Binding("MajorLineSize") { Source = this });
                    tickLine.SetBinding(Line.VisibilityProperty, new Binding("ShowMajorTicks") { Source = this, Converter = new BooleanToVisibilityConverter() });
                    Panel.SetZIndex(tickLine, 1);
                    majorTickLines.Add(tickLine);
                    this.Children.Add(tickLine);
                    if (!(i == this.m_Labels.Count - 1))
                    {
                        double minorWidth = xAxisWidthStep;
                        minorstep = minorWidth / (MinorTicksCount + 1);
                        for (int j = 0; j < this.MinorTicksCount; j++)
                        {
                            Line minorLine = new Line();
                            minorLine.Measure(new Size(this.ActualHeight, this.ActualWidth));
                            minorLine.X1 = (xAxisWidthPosition + minorstep * (j + 1));
                            minorLine.X2 = (xAxisWidthPosition + minorstep * (j + 1));
                            minorLine.Y1 = 0;
                            minorLine.SetBinding(Line.Y2Property, new Binding("MinorLineSize") { Source = this });
                            minorLine.SetResourceReference(Line.StyleProperty, "MinorLineStyle");

                            minorTickLines.Add(minorLine);
                            this.Children.Add(minorLine);
                        }
                    }
                    if (this.ShowMajorTicks)
                    {
                        labelPadding = tickLine.Y2;
                        desiredHeight = MajorLineSize;
                    }
                    Canvas.SetLeft(label, xAxisWidthPosition - (label.DesiredSize.Width));
                    Canvas.SetTop(label, desiredHeight);
                    labelSize = Math.Max(labelSize, label.DesiredSize.Height);
                    this.Children.Add(label);
                    xAxisWidthPosition += xAxisWidthStep;
                }
                if (this.Header != null)
                {
                    header = new ContentControl();
                    header.DataContext = null;
                    header.Content = this.Header;
                    header.SetBinding(ContentControl.ContentProperty, new Binding("Header") { Source = this });
                    header.SetResourceReference(ContentControl.ContentTemplateProperty, "XAxisHeaderTemplate");
                    header.Measure(new Size(this.ActualHeight, this.ActualWidth));
                    Canvas.SetLeft(header, this.ActualWidth);// (this.ActualWidth / 2) - (header.DesiredSize.Width / 2));
                    Canvas.SetTop(header, (this.ActualHeight / 2) - (header.DesiredSize.Height / 2) + desiredHeight);
                    desiredHeight += header.DesiredSize.Height + labelSize;
                    this.Children.Add(header);
                }
                this.Children.Add(axisLine);
                isInitialized = true;
            }
            if (this.Chart.AxisHeight < desiredHeight)
                this.Chart.AxisHeight = desiredHeight + 1;
        }


        public static Rect BoundsRelativeTo(FrameworkElement element, Visual relativeTo)
        {
            return element.TransformToVisual(relativeTo).TransformBounds(LayoutInformation.GetLayoutSlot(element));
        }

        internal override void InvalidateVisuals()
        {
            if (!isInitialized)
                Initialize();
            else
                Update();
        }

        public override double DataToPoint(double value)
        {
            if (this.Type == XType.String)
            {
                if (!(m_MinValue == m_MaxValue))
                {
                    var a = this.ActualWidth / (m_MaxValue + 2);
                    return a + a * value;
                }
                else
                    return 0;
            }
            else
            {
                if (!(m_MinValue == m_MaxValue))
                    return ((value - m_MinValue) * (this.ActualWidth / (m_MaxValue - m_MinValue)));
                else
                    return 0;
            }

        }


        public override void CalculateIntervalFromSeriesPoints()
        {
            List<double> xValues = new List<double>();
            if (this.Series != null)
                foreach (BaseSeries series in Series)
                {
                    if (series.Points != null)
                        foreach (var point in series.Points)
                        {
                            xValues.Add(point.XValue);
                        }
                }
            if (xValues.Count > 0)
                this.AddMinMax(xValues.ToArray().Min(), xValues.ToArray().Max());
            else if (this.Type == XType.DateTime)
                this.AddMinMax(DateTime.Now.AddHours(-1).ToOADate(), DateTime.Now.ToOADate());
        }

        override public string GetOriginalLabel(double value)
        {
            switch (Type)
            {
                case XType.Double:
                    return value.ToString(this.StringFormat);
                case XType.DateTime:
                    return DateTime.FromOADate(value).ToString(this.StringFormat);
                case XType.String:
                    if (Chart.ActualStringValues.Count > (int)value)
                        return Chart.ActualStringValues[(int)value];
                    else
                        return "";
                default:
                    return value.ToString(this.StringFormat);
            }
        }

        public XType Type
        {
            get { return (XType)GetValue(TypeProperty); }
            set { SetValue(TypeProperty, value); }
        }
        public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type", typeof(XType), typeof(XAxis), new PropertyMetadata(XType.Double, OnTypeChanged));
        private static void OnTypeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            (sender as XAxis).TypeChanged(args);
        }
        internal void TypeChanged(DependencyPropertyChangedEventArgs args)
        {
            switch (Type)
            {
                case XType.Double:
                    this.ActualType = (ActualType)Enum.Parse(typeof(ActualType), XType.Double.ToString());
                    break;
                case XType.DateTime:
                    this.ActualType = (ActualType)Enum.Parse(typeof(ActualType), XType.DateTime.ToString());
                    break;
                case XType.String:
                    this.ActualType = (ActualType)Enum.Parse(typeof(ActualType), XType.String.ToString());
                    break;
                default:
                    break;
            }
        }

    }
}
